import { useState, useEffect } from "react"

const UseEffectDemo = () => {
  const [count, setCount] = useState(0)
  const [name, setName] = useState("张三")
  const [timer, setTimer] = useState<number | null>(null)
  const [isSubscribed, setIsSubscribed] = useState(false)

  // 1. 无依赖数组: 每次渲染后都会执行
  useEffect(() => {
    console.log("1. 每次渲染后都会执行 (无依赖数组)")
    // 清理函数
    return () => {
      console.log("清理上一次的副作用")
    }
  })

  // 2. 空依赖数组: 仅在组件挂载时执行一次
  useEffect(() => {
    console.log("2. 仅在组件挂载时执行一次 (空依赖数组)")

    return () => {
      console.log("仅在组件卸载时执行清理")
    }
  }, [])

  // 3. 有依赖的数组: 在依赖变化时执行
  useEffect(() => {
    console.log(`3. count 变化时执行, 当前count: ${count}`)

    return () => {
      console.log(`清理之前 count 相关的副作用，之前的count: ${count}`)
    }
  }, [count])

  // 4. 定时器示例 - 演示清理函数的重要性
  const startTimer = () => {
    if (timer) {
      clearInterval(timer)
    }

    const id = window.setInterval(() => {
      setCount((prevCount) => prevCount + 1)
    }, 1000)

    setTimer(id)
  }

  const stopTimer = () => {
    if (timer) {
      clearInterval(timer)
      setTimer(null)
    }
  }

  // 模拟组件卸载时清理定时器
  useEffect(() => {
    return () => {
      if (timer) {
        clearInterval(timer)
      }
    }
  }, [timer])

  // 5. 模拟订阅/取消订阅API
  useEffect(() => {
    if (isSubscribed) {
      console.log("已订阅数据源")

      // 真实场景中可能是 WebSocket 连接或事件监听器
      const fakeSubscription = {
        unsubscribe: () => console.log("取消订阅数据源"),
      }

      return () => {
        fakeSubscription.unsubscribe()
      }
    }
  }, [isSubscribed])

  // useEffect(() => {
  //   // 正确方式
  //   const fetchData = async () => {
  //     const response = await api.getData()
  //     setData(response)
  //   }
  //   fetchData()
  // }, [])

  return (
    <div className="demo-section">
      <h2>useEffect Hook 面试题讲解</h2>

      <div className="card">
        <h3>1. 什么是 useEffect?</h3>
        <p>
          useEffect
          用于处理组件中的副作用，如数据获取、订阅、DOM操作等。它接收两个参数：一个副作用函数和一个依赖数组。
        </p>

        <div className="code-example">
          <code>
            useEffect(() =&gt; {"{"}
            // 副作用代码 return () =&gt; {"{"}
            // 清理函数 (可选)
            {"}"};{"}"}, [依赖1, 依赖2]);
          </code>
        </div>
      </div>

      <div className="card">
        <h3>2. 常见面试问题: useEffect 的依赖数组有什么作用?</h3>

        <p>
          当前计数: <strong>{count}</strong>
        </p>
        <p>
          姓名: <strong>{name}</strong>
        </p>

        <button onClick={() => setCount(count + 1)}>增加计数</button>
        <button onClick={() => setName(name === "张三" ? "李四" : "张三")}>
          切换姓名
        </button>

        <div className="example-output">
          <p>依赖数组控制 useEffect 何时执行:</p>
          <ul>
            <li>
              <code>useEffect(fn)</code> - 每次渲染后都执行
            </li>
            <li>
              <code>useEffect(fn, [])</code> - 仅在组件挂载时执行一次
            </li>
            <li>
              <code>useEffect(fn, [count])</code> - 仅在 count 变化时执行
            </li>
          </ul>
          <p>尝试点击不同按钮，观察控制台输出中的执行情况</p>
        </div>
      </div>

      <div className="card">
        <h3>3. 常见面试问题: 为什么需要清理函数?</h3>

        <p>
          清理函数用于防止内存泄漏，例如取消订阅、清除定时器、移除事件监听器等。
        </p>

        <div className="example-output">
          <p>定时器示例 (每秒递增计数):</p>
          <button onClick={startTimer} disabled={timer !== null}>
            启动定时器
          </button>
          <button onClick={stopTimer} disabled={timer === null}>
            停止定时器
          </button>
        </div>

        <div className="code-example">
          <code>
            useEffect(() =&gt; {"{"}
            const id = setInterval(() =&gt; {"{"}
            // 某些操作
            {"}"}, 1000); return () =&gt; {"{"}
            clearInterval(id); // 清理定时器
            {"}"};{"}"}, []);
          </code>
        </div>
      </div>

      <div className="card">
        <h3>4. 常见面试问题: useEffect 中的异步操作</h3>

        <p>
          useEffect 中可以使用异步函数，但不能直接将 useEffect 的回调设为 async
          函数。 正确做法是在内部定义并调用一个异步函数：
        </p>

        <div className="code-example">
          <code>
            useEffect(() =&gt; {"{"}
            // 正确方式 const fetchData = async () =&gt; {"{"}
            const response = await api.getData(); setData(response);
            {"}"}; fetchData(); // 错误方式 // async () =&gt; {"{"}
            // const data = await api.getData(); // {"}"}
            {"}"}, []);
          </code>
        </div>
      </div>

      <div className="card">
        <h3>5. 实际场景: 数据订阅/取消订阅</h3>

        <p>在实际应用中，useEffect 常用于管理外部数据源的订阅和取消订阅。</p>

        <div className="example-output">
          <p>
            当前订阅状态: <strong>{isSubscribed ? "已订阅" : "未订阅"}</strong>
          </p>
          <button onClick={() => setIsSubscribed(!isSubscribed)}>
            {isSubscribed ? "取消订阅" : "订阅数据"}
          </button>
          <p>切换订阅状态，观察控制台输出中的执行情况</p>
        </div>
      </div>

      <div className="card">
        <h3>6. useEffect 面试核心要点</h3>
        <ul>
          <li>
            依赖数组控制 useEffect 何时执行，应包含所有在副作用中使用的变量
          </li>
          <li>缺少依赖可能导致过时闭包问题</li>
          <li>
            清理函数在组件卸载前或副作用重新执行前执行，用于清理前一次执行的副作用
          </li>
          <li>
            React 18中，useEffect
            在开发模式下会执行两次，用于帮助发现清理函数中的问题
          </li>
          <li>
            useEffect 中使用的函数应该定义在内部，或者使用 useCallback 包装
          </li>
          <li>
            不要在循环、条件语句或嵌套函数中调用
            Hooks，应始终在函数组件的顶层使用
          </li>
        </ul>
      </div>
    </div>
  )
}

export default UseEffectDemo
